#include "base_conn.h"

#include <iostream>

using namespace std;

BaseConn::BaseConn(asio::io_service& ioservice_, UdpService& udpservice)
: service(ioservice_)
, tcpconn(ioservice_)
, udpconn(udpservice)
{
}

void BaseConn::giveSelfRef(BaseConnRef ref)
{
	token = Token(new ScopeExitSignal());
	token->add(boost::bind(&BaseConn::dispose, this, ref));
}

asio::ip::tcp::socket& BaseConn::getTcpConn()
{
	return tcpconn;
}

UdpSock& BaseConn::getUdpConn()
{
	return udpconn;
}

void BaseConn::startForwarding()
{
	cout << "Starting forwarding tcp: '" << tcpconn.remote_endpoint() << "'   udp: '" << udpconn.remote_endpoint() << "'" << endl;
	handleSendToUdp(token, boost::system::error_code());
	handleSendToTcp(token, boost::system::error_code());
}

void BaseConn::handleSendToUdp(Token tk, const boost::system::error_code& e)
{
	if (e || !token) {
		cerr << "Failed handleSendToUdp: " << e.message() << endl;
		close();
		return;
	}

	tcpconn.async_read_some(boost::asio::buffer(tcpbuf),
		boost::bind(&BaseConn::handleRecvFromTcp, this, token, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
	);
}

void BaseConn::handleRecvFromUdp(Token tk, const boost::system::error_code& e, size_t len)
{
	if (e || !token) {
		cerr << "Failed handleRecvFromUdp: " << e.message() << endl;
		close();
		return;
	}

	asio::async_write(tcpconn, boost::asio::buffer(udpbuf, len),
		boost::bind(&BaseConn::handleSendToTcp, this, token, boost::asio::placeholders::error)
	);
}

void BaseConn::handleSendToTcp(Token tk, const boost::system::error_code& e)
{
	if (e || !token) {
		cerr << "Failed handleSendToTcp: " << e.message() << endl;
		close();
		return;
	}

	udpconn.async_read_some(boost::asio::buffer(udpbuf),
		boost::bind(&BaseConn::handleRecvFromUdp, this, token, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
	);
}

void BaseConn::handleRecvFromTcp(Token tk, const boost::system::error_code& e, size_t len)
{
	if (e || !token) {
		cerr << "Failed handleRecvFromTcp: " << e.message() << endl;
		close();
		return;
	}

	asio::async_write(udpconn, boost::asio::buffer(tcpbuf, len),
		boost::bind(&BaseConn::handleSendToUdp, this, token, boost::asio::placeholders::error)
	);
}

void BaseConn::close()
{
	token.reset();
	tcpconn.close();
	udpconn.close();
}

void BaseConn::dispose(BaseConnRef ref)
{
	service.post(
		boost::bind(&BaseConn::refClear, ref)
	);
	ref.reset();
}

void BaseConn::refClear(BaseConnRef ref)
{
	ref.reset();
}

BaseConn::~BaseConn()
{
	cout << "Connection object destroyed" << endl;
}
